#include <common.h>
#include <asm/errno.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/fb.h>
#include <linux/compat.h>
#include <asm/io.h>
#include <malloc.h>

#include <lcm_splc780d.h>

/* gpio寄存器 */
#define LS1X_GPIO_BASE	0x1fd01000	/* 物理地址(4K对齐) */
#define LS1X_GPIO_CFG0	(0xc0)	/* 配置寄存器 */
#define LS1X_GPIO_OE0	(0xd0)	/* 输入使能寄存器 */
#define LS1X_GPIO_IN0	(0xe0)	/* 输入寄存器 */
#define LS1X_GPIO_OUT0	(0xf0)	/* 输出寄存器 */

/* lcd引脚对应的gpio号 */
#define LCD_RS (16)
#define LCD_RW (17)
#define LCD_EN (18)
#define LCD_D0 (19)
#define LCD_D1 (20)
#define LCD_D2 (21)
#define LCD_D3 (22)
#define LCD_D4 (23)
#define LCD_D5 (6)
#define LCD_D6 (5)
#define LCD_D7 (7)

/* 上层应用使用mmap的方式控制gpio，驱动lcd显示 */
void *gpio_map_base;

void splc780d_pin_init(void)
{
	// 使能对应的gpio，并设置为输出高电平
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_CFG0) |= (0x7<<5);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) &= ~(0x7<<5);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |= (0x7<<5);

	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_CFG0) |= (0xff<<16);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) &= ~(0xff<<16);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |= (0xff<<16);

	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_EN); //E引脚置低
}

static void splc780d_pin_set(unsigned char val)
{
	// lcdd0-lcdd7 清零 
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_D0);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_D1);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_D2);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_D3);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_D4);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_D5);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_D6);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_D7);
	// 赋值
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |= (unsigned int)((val >> 0) & 0x01) << LCD_D0;
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |= (unsigned int)((val >> 1) & 0x01) << LCD_D1;
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |= (unsigned int)((val >> 2) & 0x01) << LCD_D2;
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |= (unsigned int)((val >> 3) & 0x01) << LCD_D3;
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |= (unsigned int)((val >> 4) & 0x01) << LCD_D4;
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |= (unsigned int)((val >> 5) & 0x01) << LCD_D5;
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |= (unsigned int)((val >> 6) & 0x01) << LCD_D6;
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |= (unsigned int)((val >> 7) & 0x01) << LCD_D7;
}

static void splc780d_busy_flag(void)
{
	unsigned int value = 0;
	unsigned char data = 0;

	// lcdd0-lcdd7 设置为输入
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) |= (0x1<<LCD_D0);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) |= (0x1<<LCD_D1);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) |= (0x1<<LCD_D2);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) |= (0x1<<LCD_D3);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) |= (0x1<<LCD_D4);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) |= (0x1<<LCD_D5);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) |= (0x1<<LCD_D6);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) |= (0x1<<LCD_D7);

	// 使能读
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_RS); //RS 0
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |=  (0x1<<LCD_RW); //RW 1
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |=  (0x1<<LCD_EN); //EN 1
	udelay(800);

	do {
	value =	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_IN0);
	data |= (((value >> LCD_D0) & 0x01) << 0);
	data |= (((value >> LCD_D1) & 0x01) << 1);
	data |= (((value >> LCD_D2) & 0x01) << 2);
	data |= (((value >> LCD_D3) & 0x01) << 3);
	data |= (((value >> LCD_D4) & 0x01) << 4);
	data |= (((value >> LCD_D5) & 0x01) << 5);
	data |= (((value >> LCD_D6) & 0x01) << 6);
	data |= (((value >> LCD_D7) & 0x01) << 7);
	} while (data & 0x80);

	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_EN); //EN 0
	udelay(100);
	// lcdd0-lcdd7 设置为输出
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) &= ~(0x1<<LCD_D0);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) &= ~(0x1<<LCD_D1);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) &= ~(0x1<<LCD_D2);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) &= ~(0x1<<LCD_D3);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) &= ~(0x1<<LCD_D4);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) &= ~(0x1<<LCD_D5);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) &= ~(0x1<<LCD_D6);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OE0) &= ~(0x1<<LCD_D7);
}

void splc780d_w_cmd(unsigned char cmd)
{
	splc780d_pin_set(cmd);
	
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_RS); //RS 0
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_RW); //RW 0
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |=  (0x1<<LCD_EN); //EN 1
	udelay(800);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_EN); //EN 0
	udelay(100);
}

void splc780d_w_dat(unsigned char dat)
{
	splc780d_pin_set(dat);
	
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |=  (0x1<<LCD_RS); //RS 1
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_RW); //RW 0
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) |=  (0x1<<LCD_EN); //EN 1
	udelay(800);
	*(volatile unsigned int *)(gpio_map_base + LS1X_GPIO_OUT0) &= ~(0x1<<LCD_EN); //EN 0
	udelay(100);
}

/* 初始化lcd */
void lcd_init(void)
{
	gpio_map_base = 0xbfd01000;
	splc780d_pin_init();

	splc780d_w_cmd(0x38); //Function set: 8bit tow-line 5x8
	splc780d_w_cmd(0x0c); //Display ON/OFF control: on 光标关 Blinks开
	splc780d_w_cmd(0x01); //claer
	splc780d_w_cmd(0x06); //Entry mode
}

/* 清空屏幕显示 */
void lcd_clear(void)
{
	splc780d_w_cmd(0x01);
}

/* 设置显示位置  
  y表示第几行，值的范围是0-1  
  x表示第几列，值的范围是0-15 */
void lcd_set_xy(int x, int y)
{
	if (y & 0x1) {
		splc780d_w_cmd(0xC0 | (x & 0xf)); //行1
	} else {
		splc780d_w_cmd(0x80 | (x & 0xf)); //行0
	}
}

/* 显示一个字符 */
void lcd_show_char(unsigned char _char)
{
	splc780d_w_dat(_char);
}

/* 在指定位置显示一个字符 */
void lcd_show_char_xy(int x, int y, unsigned char _char)
{
	lcd_set_xy(x, y);
	splc780d_w_dat(_char);
}

/* 显示字符串 */
void lcd_show_str(unsigned char *str)
{
	unsigned char *s = str;
	
	while (*s) {
		splc780d_w_dat(*s);
		s++;
	}
}

/* 在指定位置显示字符串 */
void lcd_show_str_xy(int x, int y, unsigned char *str)
{	
	lcd_set_xy(x, y);
	lcd_show_str(str);
}
/*
int main(void)
{
	int fd;

	//通过mmap的方式映射gpio寄存器的地址到应用层
	if ((fd = open("/dev/mem", O_RDWR|O_SYNC)) == -1) {
		return(-1);
	}
	gpio_map_base = mmap(NULL, 0xff, PROT_READ|PROT_WRITE, MAP_SHARED, fd, LS1X_GPIO_BASE);
	
	printf("gpio base mmap is %p\n", gpio_map_base);

	lcd_init();
	lcd_show_char('L');
	sleep(2);
	lcd_set_xy(0, 1);
	lcd_show_str((unsigned char *)"welocme");

	while (1) {
		sleep(1);
		lcd_clear();
		lcd_show_str_xy(2, 0, (unsigned char *)"welocme");
		sleep(1);
		lcd_clear();
		lcd_show_str_xy(0, 1, (unsigned char *)"welocme");
	}
	
	close(fd);
	munmap(gpio_map_base, 0xff);//解除映射关系
}
*/
